package cn.nkpro.elcube.utils;

import java.net.InetAddress;
import java.security.SecureRandom;
import java.text.DecimalFormat;
import java.text.SimpleDateFormat;
import java.util.Date;

/**
 * 从org.hibernate.id.cn.nkpro.elcube.utils.UUIDHexGenerator.java中搬来
 * 与java.util.UUID生成的uuid的不同：本类生成id有序，java.util.UUID无序
 * <b>uuid</b><br>
 * <br>
 * A <tt>UUIDGenerator</tt> that returns a string of length 32,
 * This string will consist of only hex digits. Optionally,
 * the string may be generated with separators between each
 * component of the UUID.
 *
 * Mapping parameters supported: separator.
 *
 * @author Gavin King
 */
public class UUIDHexGenerator {

	private static final int IP;
	
	private static String formatedIP;
	
	private static final int JVM = (int) ( System.currentTimeMillis() >>> 8 );
	
	private static String formatedJVM;

	private static short counter = (short) 0;

	private static String suffix = null;
	
	static {
		int ipadd;
		try {
			ipadd = toInt( InetAddress.getLocalHost().getAddress() );
		}
		catch (Exception e) {
			ipadd = 0;
		}
		IP = ipadd;
		formatedIP = format( getIP());
		formatedJVM = format( getJVM());
	}

	public static String nonceTimestamp(){
		return new SimpleDateFormat("yyyyMMddHHmmssSSS").format(new Date())+
				new DecimalFormat("00000").format(Math.random()*99999);
	}

	public static String nonce(){
		String all 	  = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
		String num    = "0123456789";
		String letter = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
		SecureRandom secureRandom = new SecureRandom();
		StringBuilder uuid = new StringBuilder();
		for (int j = 0 ;j<32;j++){
			int i  = secureRandom.nextInt(3);
			int i1 = secureRandom.nextInt(10);
			int i2 = secureRandom.nextInt(52);
			int i3 = secureRandom.nextInt(62);
			switch (i){
				case 0:
					String substring1= num.substring(i1,i1+1);
					uuid.append(substring1);
					break;
				case 1:
					String substring2= letter.substring(i2,i2+1);
					uuid.append(substring2);
					break;
				case 2:
					String substring3= all.substring(i3,i3+1);
					uuid.append(substring3);
					break;
				default:
					throw new RuntimeException();
			}
		}
		return uuid.toString();
	}
 
	public static String generate() {
		String sep = "-";
		return    formatedJVM + sep
				+ formatedIP + sep
				+ format( getHiTime() ) + sep
				+ format( getLoTime() ) + sep
				+ format( getCount() )
				+ getSuffix();
	}

	private static String getSuffix(){
		return suffix == null ? "" : suffix;
	}
 
	private static String format(int intValue) {
		String formatted = Integer.toHexString( intValue );
		StringBuilder buf = new StringBuilder( "00000000" );
		buf.replace( 8 - formatted.length(), 8, formatted );
		return buf.toString();
	}
 
	private static String format(short shortValue) {
		String formatted = Integer.toHexString( shortValue );
		StringBuilder buf = new StringBuilder( "0000" );
		buf.replace( 4 - formatted.length(), 4, formatted );
		return buf.toString();
	}
	
	/**
	 * Unique across JVMs on this machine (unless they load this class
	 * in the same quater second - very unlikely)
	 */
	private static int getJVM() {
		return JVM;
	}

	/**
	 * Unique in a millisecond for this JVM instance (unless there
	 * are > Short.MAX_VALUE instances created in a millisecond)
	 */
	private static short getCount() {
		synchronized(UUIDHexGenerator.class) {
			if (counter<0) counter=0;
			return counter++;
		}
	}
 
	/**
	 * Unique in a local network
	 */
	private static int getIP() {
		return IP;
	}
 
	/**
	 * Unique down to millisecond
	 */
	private static short getHiTime() {
		return (short) ( System.currentTimeMillis() >>> 32 );
	}
	private static int getLoTime() {
		return (int) System.currentTimeMillis();
	}

//	/**
//	 * Get the config value as a {@link String}
//	 *
//	 * @param name The config setting name.
//	 * @param values The map of config values
//	 *
//	 * @return The value, or null if not found
//	 */
//	private String getString(String name, Map values) {
//		Object value = values.get( name );
//		if ( value == null ) {
//			return null;
//		}
//		if (value instanceof String) {
//			return (String) value;
//		}
//		return value.toString();
//	}
	
	/**
	 * Custom algorithm used to generate an int from a series of bytes.
	 * <p/>
	 * NOTE : this is different than interpreting the incoming bytes as an int value!
	 *
	 * @param bytes The bytes to use in generating the int.
	 *
	 * @return The generated int.
	 */
	private static int toInt(byte[] bytes) {
		int result = 0;
		for ( int i = 0; i < 4; i++ ) {
			result = ( result << 8 ) - Byte.MIN_VALUE + (int) bytes[i];
		}
		return result;
	}
}